#ifndef __POLLER_HPP__
#define __POLLER_HPP_

#include <vector>
#include <unordered_map>
#include <sys/epoll.h>
#include "channel.hpp"

#define MAX_EPOLLEVENTS 1024
class Poller
{
private:
    /* 对epoll的直接操作 */
    void Update(Channel *channel, int op)
    {
        int fd = channel->Fd(); // 要关心的文件描述符
        struct epoll_event ev;  // 要监控的事件
        ev.events = channel->Events();
        ev.data.fd = fd;
        int ret = epoll_ctl(_epfd, op, fd, &ev);
        if (ret < 0)
            ERR_LOG("epoll_ctl [op:%d] [fd:%d] failed [%d]: %s", op, fd, errno, strerror(errno));
    }
    /* 判断一个Channel是否已经添加了事件监控 */
    bool HasChannel(Channel *channel) const
    {
        auto it = _channels.find(channel->Fd());
        if (it == _channels.end())
            return false;
        return true;
    }

public:
    Poller()
    {
        _epfd = epoll_create(MAX_EPOLLEVENTS);
        if(_epfd < 0)
        {
            ERR_LOG("epoll create failed [%d]: %s", errno, strerror(errno));
            exit(ERROR_EPOLL);
        }
    }
    /* 更新文件描述符上的事件监控（不存在就添加） */
    void UpdateEvent(Channel *channel)
    {
        if (HasChannel(channel)) // 更新
            Update(channel, EPOLL_CTL_MOD);
        // 添加
        else
        {
            _channels.insert(std::make_pair(channel->Fd(), channel));
            // DBG_LOG("_channels.insert(%d, channle)", channel->Fd());
            Update(channel, EPOLL_CTL_ADD);
        }
    }
    /* 移除文件描述符上的事件监控 */
    void RemoveEvent(Channel *channel)
    {
        // 从epoll中移除对事件的管理
        Update(channel, EPOLL_CTL_DEL);
        // 从_channel哈希表中移除
        _channels.erase(channel->Fd()); 
    }
    /* 开始对事件进行监控，返回活跃连接 */
    void Poll(std::vector<Channel*>& actives)
    {
        int n = epoll_wait(_epfd, _events, MAX_EPOLLEVENTS, -1);// 阻塞式等待
        if(n < 0)
        {
            if(errno == EINTR) return;
            ERR_LOG("epoll wait failed [%d]: %s", errno, strerror(errno));
            exit(ERROR_EPOLL);
        }
        // DBG_LOG("n: %d", n);
        for(int i = 0; i < n; ++i)
        {
            auto it = _channels.find(_events[i].data.fd);
            // DBG_LOG("_events[%d].data.fd: %d", i, _events[i].data.fd);
            assert(it != _channels.end());

            // 设置就绪事件
            it->second->SetREvents(_events[i].events);
            // 设置/添加活跃连接
            actives.push_back(it->second);
        }
    }

private:
    int _epfd;                                    // epoll操作句柄
    struct epoll_event _events[1024];             // 保存监控时所有的活跃事件
    std::unordered_map<int, Channel *> _channels; // 保存描述符与描述符对应的Channel
};

#endif